#include "atjenie_parser.h"
//#include <windows.h>
#include <QtCore/QtCore>

//bool ATJenie_Parser::WaitForAndReadLine(QString& Buffer, int iTimeout)
//{
//    QDateTime startTime = QDateTime::currentDateTime();
//    bool bTerminated = false;
//    Buffer.clear();

//    while (startTime.msecsTo(QDateTime::currentDateTime()) < iTimeout && !bTerminated)
//    {
//        if (Port_.bytesAvailable() > 0)
//            Buffer += Port_.read(1);

//        if (Buffer.endsWith("\n"))
//            bTerminated = true;
//    }
//    // No bytes came
    
//    return bTerminated;
//}

ATJenie_Parser::ATJenie_Parser(QextSerialPort& Port, QObject *parent)
    : QObject(parent), Port_(Port)
{

    if (!Port_.open(QIODevice::ReadWrite/* | QIODevice::Unbuffered*/))
            throw QString(tr("Can\t open!"));
    connect(&Port_,SIGNAL(readyRead()), this, SLOT(ProcessLine()));
    connect(this, SIGNAL(NetworkCreated(AtJenie_NTU)),
            this, SLOT(NetworkOk()));
    connect(this, SIGNAL(NetworkJoined(AtJenie_NTU)),
            this, SLOT(NetworkOk()));
    //Port_.setQueryMode(QextSerialPort::EventDriven);
    Port_.setFlowControl(FLOW_OFF);
    Port_.reset();
    qDebug("%d",sizeof(Port_));
}

QTextStream& BinaryArgument(QTextStream& stream, void *pDatam, size_t stDataSize)
{
    //Do something useful to convert binary data into hex-string

    return stream;
}

bool ATJenie_Parser::SendReset()
{
	int Attempts = 0;

    QString AnsLine;
	
	while (Attempts < 2)
	{
		// Write reset command
		Port_.write("RST\n");

                if (!WaitForResult(AnsLine, 1000))
                        throw QString(tr("Can\'t read!"));

                if (AnsLine.startsWith("ERR"))
                {
                        Attempts++;
                        continue;
                }
                else
               {

                   if (!AnsLine.startsWith("RST"))
                   {
                       //if (!WaitForResult(AnsLine, 1000))
                        //       throw QString(tr("Can\'t read!"));
                       if (!AnsLine.startsWith("AT-Jenie"))
                       {
                            throw QString("Rubbish!");
                       }
                       else
                               break;
                   }
               }


		// Wait some more time for next line
                if (!WaitForResult(AnsLine, 1000))
			throw QString(tr("Can\'t read answer!"));

		if (!AnsLine.startsWith("AT-Jenie"))
		{
                    if (AnsLine.startsWith("ERR"))
                    {
                            Attempts++;
                            continue;
                    }
                    else
                        throw QString("Unknown answer");
		}
		else
			break;
	}
	return (Attempts < 2);    
}

bool ATJenie_Parser::SendCommand(QString Command)
{
	qDebug("Sending command: "+Command.toAscii());
        QString fakeCommand;
        fakeCommand = Command;
        Port_.write(fakeCommand.toAscii());
	
	QString AnsLine;
        //if (!WaitForAndReadLine(AnsLine, 1000))
                //throw QString(tr("Can\'t read echo!"));
        //if (AnsLine != Command)
                //throw QString(tr("Invalid echo output!"));

        if (!WaitForResult(AnsLine, 1000))
		throw QString(tr("Can\'t read answer!"));

	if (!AnsLine.startsWith("OK"))
		return false;
	qDebug("Success");
	return true;
}

void ATJenie_Parser::ProcessLine()
{
        drFlag = true;
        qDebug("Data available %d",Port_.bytesAvailable());
        static QString st;
        int i = 1;
        st += Port_.read(Port_.bytesAvailable());
        qDebug("%d",i++);
        qDebug(st.toAscii());


        QStringList Answers = st.split("\n");
        st = Answers.back();
        Answers.removeLast();
        while(!Answers.empty())
        {
            QString CurrentString = Answers.front().trimmed();
            if (CurrentString.startsWith("OK") || CurrentString.startsWith("ERR") ||
                    CurrentString.startsWith("RST") || CurrentString.startsWith("AT-Jenie") ||
                    CurrentString.startsWith("CCF"))
            {
                ResultStack.push(CurrentString);

            }
            else
            {
                if (!CurrentString.startsWith("TNR"))
                {
                    QStringList Message = CurrentString.split(',');
                    if (Message[ATJENIE_MESSAGE_TYPE] == "NTU")
                    {
                           qDebug("Recieved NRU message with %u arguments",
                                  Message.size() - 1);

                            AtJenie_NTU NTU;
                            bool bOk;
                            NTU.u64AddressParent = Message[1].toULongLong(&bOk, 16);
                            if (!bOk)
                                throw;
                            NTU.u64AddressNode = Message[2].toULongLong(&bOk, 16);
                            if (!bOk)
                                throw;
                            NTU.u8Depth = Message[3].toInt(&bOk, 16);
                            if (!bOk)
                                throw;
                            NTU.u32PANID = Message[4].toULong(&bOk, 16);
                            if (!bOk)
                                throw;
                            NTU.u8Channel = Message[5].toInt(&bOk, 16);
                            if (!bOk)
                                throw;

                            if (NTU.u8Depth)
                                NetworkJoined(NTU);
                            else
                                NetworkCreated(NTU);

                    }
                    if (Message[ATJENIE_MESSAGE_TYPE] == "RST")
                    {
                           qDebug("Recieved stack reset message with %u arguments",
                                  Message.size() - 1);

                           StackReset();
                    }
                    if (Message[ATJENIE_MESSAGE_TYPE] == "DAT")
                    {
                           qDebug("Recieved new data message with %u arguments",
                                  Message.size() - 1);

                           if (Message[3] == "0")
                           {
                               Answers.removeFirst();
                               continue;
                           }
                           QByteArray Data;
                           Data.fromHex(QByteArray(Message[4].toAscii()));
                           bool bOk;
                           uint64_t u64AddressSource = Message[1].toULongLong(&bOk, 16);
                           if (!bOk)
                               throw;

                           NewData(u64AddressSource, Data);
                    }
                    if (Message[ATJENIE_MESSAGE_TYPE] == "DTS")
                    {
                           qDebug("Recieved new data message with %u arguments",
                                  Message.size() - 1);

                           AtJenie_DTS_Header DTS_Header;
                           bool bOk;
                           DTS_Header.u64AddressSource = Message[1].toULongLong(&bOk, 16);
                           if (!bOk)
                               throw;
                           DTS_Header.u8SourceChannel = Message[2].toUInt(&bOk, 16);
                           if (!bOk)
                               throw;
                           DTS_Header.u8RemoteChannel = Message[3].toUInt(&bOk, 16);
                           if (!bOk)
                               throw;

                           QByteArray Data;
                           Data.fromHex(QByteArray(Message[5].toAscii()));

                           NewData(DTS_Header, Data);
                    }
                    if (Message[ATJENIE_MESSAGE_TYPE] == "CHJ")
                    {
                           qDebug("Recieved new child join message with %u arguments",
                                  Message.size() - 1);

                           bool bOk;
                           uint64_t u64Address = Message[1].toULongLong(&bOk, 16);
                           if (!bOk)
                               throw;

                           ChildJoined(u64Address);
                    }
                    if (Message[ATJENIE_MESSAGE_TYPE] == "CHL")
                    {
                           qDebug("Recieved new child left message with %u arguments",
                                  Message.size() - 1);

                           bool bOk;
                           uint64_t u64Address = Message[1].toULongLong(&bOk, 16);
                           if (!bOk)
                               throw;

                           ChildLeft(u64Address);
                    }
                    if (Message[ATJENIE_MESSAGE_TYPE] == "CHR")
                    {
                           qDebug("Recieved new child join message with %u arguments",
                                  Message.size() - 1);

                           bool bOk;
                           uint64_t u64Address = Message[1].toULongLong(&bOk, 16);
                           if (!bOk)
                               throw;

                           ChildRejected(u64Address);
                    }
                    if (Message[ATJENIE_MESSAGE_TYPE] == "RSR")
                    {
                           qDebug("Recieved register service response message with %u arguments",
                                  Message.size() - 1);

                           RegisterServiceResponse();
                    }
                    if (Message[ATJENIE_MESSAGE_TYPE] == "SRR")
                    {
                           qDebug("Recieved service request response message with %u arguments",
                                  Message.size() - 1);

                           bool bOk;
                           uint64_t u64Address = Message[1].toULongLong(&bOk, 16);
                           if (!bOk)
                               throw;
                           uint32_t u32ServiceMask = Message[2].toULong(&bOk, 16);
                           if (!bOk)
                               throw;

                           ServiceRequestResponse(u64Address, u32ServiceMask);
                    }
                    if (Message[ATJENIE_MESSAGE_TYPE] == "PKS")
                    {
                           qDebug("Recieved data packet send succesful message with %u arguments",
                                  Message.size() - 1);

                           DataPacketSend(true);
                    }
                    if (Message[ATJENIE_MESSAGE_TYPE] == "PKF")
                    {
                           qDebug("Recieved data packet send failed message with %u arguments",
                                  Message.size() - 1);

                           DataPacketSend(false);
                    }
                    if (Message[ATJENIE_MESSAGE_TYPE] == "ACK")
                    {
                           qDebug("Recieved data acknowledgement message with %u arguments",
                                  Message.size() - 1);

                           DataAcknowledgement(false);
                    }
                    if (Message[ATJENIE_MESSAGE_TYPE] == "SAK")
                    {
                           qDebug("Recieved data acknowledgement from service message with %u arguments",
                                  Message.size() - 1);

                           DataAcknowledgement(true);
                    }
                    if (Message[ATJENIE_MESSAGE_TYPE] == "POL")
                    {
                           qDebug("Recieved polling message with %u arguments",
                                  Message.size() - 1);

                           bool bOk;
                           uint8_t u8PollStatus = Message[1].toUInt(&bOk, 16);
                           if (!bOk)
                               throw;

                           Polling(u8PollStatus);
                    }
                    if (Message[ATJENIE_MESSAGE_TYPE] == "PEV")
                    {
                           qDebug("Recieved harware event message with %u arguments",
                                  Message.size() - 1);

                           AtJenie_HardwareEvent Event;
                           bool bOk;
                           Event.u8Source = Message[1].toUInt(&bOk, 16);
                           if (!bOk)
                               throw;
                           Event.u32Data = Message[2].toULong(&bOk, 16);
                           if (!bOk)
                               throw;

                           HardwareEvent(Event);
                    }
                }
                else
                {
                    qDebug("Recieved tunneling message");
                    Tunneling(CurrentString.section('\"', 1, -1));
                }

            }


            Answers.removeFirst();
        }
}

bool ATJenie_Parser::ConfigureATJenie()
{
    return SendCommand("CCF,115200,1,0,0,0\n");
}

bool ATJenie_Parser::ConfigureNetwork(uint32_t u32ChannelMask, uint8_t u8Children,  uint8_t u8EndDevices,
                   uint8_t u8Failures, uint32_t u32EndDeviceTimeout)
{
    QString Command;
    QTextStream str(&Command);
        str << "CFG," << hex << uppercasedigits << u32ChannelMask << ',' << u8Children
                            << ',' << u8EndDevices << ',' << u8Failures << ','
                               << u32EndDeviceTimeout << '\n';
    return SendCommand(Command);
}

bool ATJenie_Parser::ConfigureChild(uint16_t u16PingPeriod, uint8_t u8CyclesBeforePing,
                    uint32_t u32ScanSleep, uint32_t u32PollPeriod,
                    uint8_t u8MaxNumberOfHops)
{
    QString Command;
    QTextStream str(&Command);
        str << "CFP," << hex << uppercasedigits << u16PingPeriod << ',' << u8CyclesBeforePing
                            << ',' << u32ScanSleep << ',' << u32PollPeriod << ','
                               << u8MaxNumberOfHops << '\n';
    return SendCommand(Command);
}

bool ATJenie_Parser::Initialize(uint16_t u16PanID, uint8_t u8Channel,
                    uint32_t u32NetAppID, uint8_t u8RestoreContext,
                    uint8_t u8Routing)
{
    QString Command;
    QTextStream str(&Command);
        str << "INI," << hex << uppercasedigits << u16PanID << ',' << u8Channel
                            << ',' << u32NetAppID << ',' << u8RestoreContext << ','
                               << u8Routing << '\n';
    return SendCommand(Command);
}

bool ATJenie_Parser::StartDevice(ATJenie_DeviceType DeviceType)
{
    QString Command;
    QTextStream str(&Command);
        str << "STR," << hex << uppercasedigits << static_cast<int>(DeviceType) << '\n';
    return SendCommand(Command);
}

bool ATJenie_Parser::CreateNetwork(uint16_t u16PanID, uint8_t u8Channel,
                   uint32_t u32NetAppID, uint8_t u8RestoreContext, uint8_t u8Routing,
                   uint32_t u32ChannelMask, uint8_t u8Children, uint8_t u8EndDevices,
                   uint8_t u8Failures, uint32_t u32EndDeviceTimeout)
{
    bNetworkOk = false;
    return ConfigureNetwork(u32ChannelMask, u8Children, u8EndDevices, u8Failures,
                  u32EndDeviceTimeout) && 
			Initialize(u16PanID, u8Channel, u32NetAppID, u8RestoreContext, u8Routing) &&
		    StartDevice(ATJenie_DeviceType_Coordinator);
}

bool ATJenie_Parser::JoinNetwork(ATJenie_DeviceType DeviceType, uint16_t u16PanID,                   uint8_t u8Channel, uint32_t u32NetAppID,
                   uint8_t u8RestoreContext, uint8_t u8Routing,
                   uint32_t u32ChannelMask, uint8_t u8Children,
                   uint8_t u8EndDevices, uint8_t u8Failures,
                   uint32_t u32EndDeviceTimeout, uint16_t u16PingPeriod,
                   uint8_t u8CyclesBeforePing, uint32_t u32ScanSleep,
                   uint32_t u32PollPeriod, uint8_t u8MaxNumberOfHops)
{
    bNetworkOk = false;
    return ConfigureNetwork(u32ChannelMask, u8Children, u8EndDevices, u8Failures,
                  u32EndDeviceTimeout) &&
			ConfigureChild(u16PingPeriod, u8CyclesBeforePing, u32ScanSleep,
                   u32PollPeriod, u8MaxNumberOfHops) &&
			Initialize(u16PanID, u8Channel, u32NetAppID, u8RestoreContext, u8Routing) &&
			StartDevice(DeviceType);
}

bool ATJenie_Parser::SendListOfRequestedServices(uint32_t u32ServiceMask,
                                 ATJenie_TypeOfMatch TypeOfMatch)
{
    QString Command;
    QTextStream str(&Command);
        str << "RQS," << hex << uppercasedigits << u32ServiceMask << ','
        << static_cast<int>(TypeOfMatch) << '\n';
    return SendCommand(Command);
}

bool ATJenie_Parser::WaitForResult(QString& Buffer, int iTimeout)
{
    QDateTime startTime = QDateTime::currentDateTime();
    while (startTime.msecsTo(QDateTime::currentDateTime()) < iTimeout && ResultStack.empty())
    {
        qApp->processEvents();
    }
    // No bytes came

    if (startTime.msecsTo(QDateTime::currentDateTime()) < iTimeout)
    {
        Buffer = ResultStack.pop();
        return true;
    }
    else
        return false;
}

bool ATJenie_Parser::RegisterServices(uint32_t u32ServiceMask)
{
    QString Command;
    QTextStream str(&Command);
        str << "REG," << hex << uppercasedigits << u32ServiceMask << '\n';
    return SendCommand(Command);
}

bool ATJenie_Parser::Bind(uint8_t u8LocalService, uint64_t u64RemoteNode,
          uint8_t u8RemoteService)
{
    QString Command;
    QTextStream str(&Command);
        str << "BND," << hex << uppercasedigits << u8LocalService << ','
        << u64RemoteNode << ',' << u8RemoteService << '\n';
    return SendCommand(Command);
}

bool ATJenie_Parser::Unbind(uint8_t u8LocalService, uint64_t u64RemoteNode,
          uint8_t u8RemoteService)
{
    QString Command;
    QTextStream str(&Command);
        str << "UBN," << hex << uppercasedigits << u8LocalService << ','
        << u64RemoteNode << ',' << u8RemoteService << '\n';
    return SendCommand(Command);
}

bool ATJenie_Parser::SendData(uint64_t u64RemoteNode, QByteArray Data, uint8_t Flags)
{
    QString Command;
    QTextStream str(&Command);
        str << "SND," << hex << uppercasedigits << u64RemoteNode << ','
            << Data.toHex().toUpper() << ',' << Data.size() << ',' << Flags << '\n';

    return SendCommand(Command);
}


bool ATJenie_Parser::SendDataToService(uint8_t u8LocalNode, QByteArray Data, uint8_t Flags)
{
    QString Command;
    QTextStream str(&Command);
    str << "SDS," << hex << uppercasedigits << u8LocalNode << ','
        << Data.toHex().toUpper() << ',' << Data.size() << ',' << Flags << '\n';
    return SendCommand(Command);
}

bool ATJenie_Parser::Polling()
{
    QString Command;
    QTextStream str(&Command);
    str << "POL," << '\n';
    return SendCommand(Command);
}

bool ATJenie_Parser::Leave()
{
    QString Command;
    QTextStream str(&Command);
    str << "LVE," << '\n';
    return SendCommand(Command);
}

bool ATJenie_Parser::WaitForNetwork(int iTimeout)
{
    QDateTime startTime = QDateTime::currentDateTime();
    while (startTime.msecsTo(QDateTime::currentDateTime()) < iTimeout && !bNetworkOk)
    {
        qApp->processEvents();
    }
    // No bytes came

    if (startTime.msecsTo(QDateTime::currentDateTime()) < iTimeout)
        return true;
    else
        return false;
}

void ATJenie_Parser::NetworkOk()
{
    bNetworkOk = true;
}
